﻿using System;
using Microsoft.Practices.Prism.Regions;

namespace UltimateNavigationFramework.UriNavigation
{
    public class UltimateRegionNavigationService : IUltimateRegionNavigationService
    {
        private IUltimateRegionNavigationJournal _journal;
        private UltimateNavigationFrame _frame;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="journal"></param>
        public UltimateRegionNavigationService(IUltimateRegionNavigationJournal journal)
        {
            this._journal = journal;
            this._journal.NavigationTarget = this;
        }

        #region IPrismRegionNavigationService Implementation
        public event EventHandler<Microsoft.Practices.Prism.Regions.RegionNavigationEventArgs> Navigated;
        public event EventHandler<Microsoft.Practices.Prism.Regions.RegionNavigationEventArgs> Navigating;
        public event EventHandler<Microsoft.Practices.Prism.Regions.RegionNavigationFailedEventArgs> NavigationFailed;

        /// <summary>
        /// Gets or sets the frame.
        /// This should be set in the Frame Region Adapter
        /// </summary>
        /// <value>The frame.</value>
        public UltimateNavigationFrame Frame
        {
            get { return this._frame; }
            set
            {
                this._frame = value;
                if (this._journal != null)
                    this._journal.Frame = this._frame;
            }
        } 

        public Microsoft.Practices.Prism.Regions.IRegionNavigationJournal Journal
        {
            get { return this._journal; }
        }

        /// <summary>
        /// Gets or sets the region owning this service.
        /// </summary>
        /// <value>A Region.</value>
        public IRegion Region { get; set; }

        /// <summary>
        /// Initiates navigation to the specified source.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <param name="navigationCallback">A callback to execute when the navigation request is completed.</param>
        public void RequestNavigate(Uri source, Action<Microsoft.Practices.Prism.Regions.NavigationResult> navigationCallback)
        {
            if (navigationCallback == null) throw new ArgumentNullException("navigationCallback");

            try
            {
                IUltimateRegionNavigationCommand command = new UltimateRegionNavigationCommand(
                    this.Frame,
                    source, 
                    new NavigationContext(this, source), 
                    navigationCallback,
                    this.RaiseNavigating,
                    this.RaiseNavigated,
                    this.RaiseNavigationFailed);

                command.RequestNavigate();
            }
            catch (Exception e)
            {
                this.NotifyNavigationFailed(new NavigationContext(this, source), navigationCallback, e);
            }
        } 
        #endregion

        #region Navigation Events execution
        /// <summary>
        /// Raise Navigated
        /// </summary>
        /// <param name="navigationContext"></param>
        private void RaiseNavigated(NavigationContext navigationContext)
        {
            var handler = this.Navigated;
            if (handler != null)
                handler(this, new RegionNavigationEventArgs(navigationContext));
        }

        /// <summary>
        /// Raise Navigating
        /// </summary>
        /// <param name="navigationContext"></param>
        private void RaiseNavigating(NavigationContext navigationContext)
        {
            var handler = this.Navigating;
            if (handler != null)
                handler(this, new RegionNavigationEventArgs(navigationContext));
        }

        /// <summary>
        /// Raise navigation failed without callback
        /// </summary>
        /// <param name="navigationContext"></param>
        /// <param name="error"></param>
        private void RaiseNavigationFailed(NavigationContext navigationContext, Exception error)
        {
            if (this.NavigationFailed != null)
                this.NavigationFailed(this, new RegionNavigationFailedEventArgs(navigationContext, error));
        }
        #endregion

        #region Notify Navigation Failed
        /// <summary>
        /// Notify navigation failed with callback notification
        /// </summary>
        /// <param name="navigationContext"></param>
        /// <param name="navigationCallback"></param>
        /// <param name="e"></param>
        private void NotifyNavigationFailed(NavigationContext navigationContext, Action<NavigationResult> navigationCallback, Exception e)
        {
            var navigationResult =
                e != null ? new NavigationResult(navigationContext, e) : new NavigationResult(navigationContext, false);

            navigationCallback(navigationResult);
            this.RaiseNavigationFailed(navigationContext, e);
        }
        #endregion   
    }
}
